﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LCQ.QueryBuilder
{
    public class SelectQueryBuilder : IQueryBuilder
    {
        private DbProviderFactory _dbProviderFactory;
        protected bool _distinct;
        protected List<string> _groupByColumns;
        protected WhereStatement _havingStatement;
        protected List<JoinClause> _joins;
        protected List<OrderByClause> _orderByStatement;
        protected List<string> _selectedColumns;
        protected List<string> _selectedTables;
        protected TopClause _topClause;
        protected WhereStatement _whereStatement;

        public SelectQueryBuilder()
        {
            this._topClause = new TopClause(100, TopUnit.Percent);
            this._selectedColumns = new List<string>();
            this._selectedTables = new List<string>();
            this._joins = new List<JoinClause>();
            this._whereStatement = new WhereStatement();
            this._orderByStatement = new List<OrderByClause>();
            this._groupByColumns = new List<string>();
            this._havingStatement = new WhereStatement();
        }

        public SelectQueryBuilder(DbProviderFactory factory)
        {
            this._topClause = new TopClause(100, TopUnit.Percent);
            this._selectedColumns = new List<string>();
            this._selectedTables = new List<string>();
            this._joins = new List<JoinClause>();
            this._whereStatement = new WhereStatement();
            this._orderByStatement = new List<OrderByClause>();
            this._groupByColumns = new List<string>();
            this._havingStatement = new WhereStatement();
            this._dbProviderFactory = factory;
        }

        public void AddHaving(WhereClause clause)
        {
            this.AddHaving(clause, 1);
        }

        public void AddHaving(WhereClause clause, int level)
        {
            this._havingStatement.Add(clause, level);
        }

        public WhereClause AddHaving(Enum field, Comparison @operator, object compareValue)
        {
            return this.AddHaving(field.ToString(), @operator, compareValue, 1);
        }

        public WhereClause AddHaving(string field, Comparison @operator, object compareValue)
        {
            return this.AddHaving(field, @operator, compareValue, 1);
        }

        public WhereClause AddHaving(string field, Comparison @operator, object compareValue, int level)
        {
            WhereClause clause = new WhereClause(field, @operator, compareValue);
            this._havingStatement.Add(clause, level);
            return clause;
        }

        public void AddJoin(JoinClause newJoin)
        {
            this._joins.Add(newJoin);
        }

        public void AddJoin(JoinType join, string toTableName, string toColumnName, Comparison @operator, string fromTableName, string fromColumnName)
        {
            JoinClause item = new JoinClause(join, toTableName, toColumnName, string.Empty, @operator, fromTableName, fromColumnName);
            this._joins.Add(item);
        }

        public void AddOrderBy(OrderByClause clause)
        {
            this._orderByStatement.Add(clause);
        }

        public void AddOrderBy(Enum field, Sorting order)
        {
            this.AddOrderBy(field.ToString(), order);
        }

        public void AddOrderBy(string field, Sorting order)
        {
            OrderByClause item = new OrderByClause(field, order);
            this._orderByStatement.Add(item);
        }

        public void AddWhere(WhereClause clause)
        {
            this.AddWhere(clause, 1);
        }

        public void AddWhere(WhereClause clause, int level)
        {
            this._whereStatement.Add(clause, level);
        }

        public WhereClause AddWhere(Enum field, Comparison @operator, object compareValue)
        {
            return this.AddWhere(field.ToString(), @operator, compareValue, 1);
        }

        public WhereClause AddWhere(string field, Comparison @operator, object compareValue)
        {
            return this.AddWhere(field, @operator, compareValue, 1);
        }

        public WhereClause AddWhere(string field, Comparison @operator, object compareValue, int level)
        {
            WhereClause clause = new WhereClause(field, @operator, compareValue);
            this._whereStatement.Add(clause, level);
            return clause;
        }

        public DbCommand BuildCommand()
        {
            return (DbCommand)this.BuildQuery(true);
        }

        public string BuildQuery()
        {
            return (string)this.BuildQuery(false);
        }

        private object BuildQuery(bool buildCommand)
        {
            if (buildCommand && (this._dbProviderFactory == null))
            {
                throw new Exception("数据工厂为空，请联系管理员");
            }
            DbCommand usedDbCommand = null;
            if (buildCommand)
            {
                usedDbCommand = this._dbProviderFactory.CreateCommand();
            }
            string str = "SELECT ";
            if (this._distinct)
            {
                str = str + "DISTINCT ";
            }
            if (!((this._topClause.Quantity == 100) & (this._topClause.Unit == TopUnit.Percent)))
            {
                str = str + "TOP " + this._topClause.Quantity;
                if (this._topClause.Unit == TopUnit.Percent)
                {
                    str = str + " PERCENT";
                }
                str = str + " ";
            }
            if (this._selectedColumns.Count == 0)
            {
                if (this._selectedTables.Count == 1)
                {
                    str = str + this._selectedTables[0] + ".";
                }
                str = str + "*";
            }
            else
            {
                foreach (string str2 in this._selectedColumns)
                {
                    str = str + str2 + ',';
                }
                str = str.TrimEnd(new char[] { ',' }) + ' ';
            }
            if (this._selectedTables.Count > 0)
            {
                str = str + " FROM ";
                foreach (string str3 in this._selectedTables)
                {
                    str = str + str3 + ',';
                }
                str = str.TrimEnd(new char[] { ',' }) + ' ';
            }
            if (this._joins.Count > 0)
            {
                foreach (JoinClause clause in this._joins)
                {
                    string str4 = "";
                    switch (clause.JoinType)
                    {
                        case JoinType.InnerJoin:
                            str4 = "INNER JOIN";
                            break;

                        case JoinType.OuterJoin:
                            str4 = "OUTER JOIN";
                            break;

                        case JoinType.LeftJoin:
                            str4 = "LEFT JOIN";
                            break;

                        case JoinType.RightJoin:
                            str4 = "RIGHT JOIN";
                            break;
                    }
                    str4 = str4 + " " + clause.ToTable + " ON ";
                    if (string.IsNullOrEmpty(clause.Alias))
                    {
                        string fieldName = clause.FromTable + '.' + clause.FromColumn;
                        str4 = str4 + WhereStatement.CreateComparisonClause(fieldName, clause.ComparisonOperator, new SqlLiteral(clause.ToTable + '.' + clause.ToColumn));
                    }
                    else
                    {
                        string introduced21 = clause.FromTable + '.' + clause.FromColumn;
                        str4 = str4 + WhereStatement.CreateComparisonClause(introduced21, clause.ComparisonOperator, new SqlLiteral(clause.Alias + '.' + clause.ToColumn));
                    }
                    str = str + str4 + ' ';
                }
            }
            if (this._whereStatement.ClauseLevels > 0)
            {
                if (buildCommand)
                {
                    str = str + " WHERE " + this._whereStatement.BuildWhereStatement(true, ref usedDbCommand);
                }
                else
                {
                    str = str + " WHERE " + this._whereStatement.BuildWhereStatement();
                }
            }
            if (this._groupByColumns.Count > 0)
            {
                str = str + " GROUP BY ";
                foreach (string str5 in this._groupByColumns)
                {
                    str = str + str5 + ',';
                }
                str = str.TrimEnd(new char[] { ',' }) + ' ';
            }
            if (this._havingStatement.ClauseLevels > 0)
            {
                if (this._groupByColumns.Count == 0)
                {
                    throw new Exception("请设置分组参数");
                }
                if (buildCommand)
                {
                    str = str + " HAVING " + this._havingStatement.BuildWhereStatement(true, ref usedDbCommand);
                }
                else
                {
                    str = str + " HAVING " + this._havingStatement.BuildWhereStatement();
                }
            }
            if (this._orderByStatement.Count > 0)
            {
                str = str + " ORDER BY ";
                foreach (OrderByClause clause2 in this._orderByStatement)
                {
                    string str6 = "";
                    switch (clause2.SortOrder)
                    {
                        case Sorting.Ascending:
                            str6 = clause2.FieldName + " ASC";
                            break;

                        case Sorting.Descending:
                            str6 = clause2.FieldName + " DESC";
                            break;
                    }
                    str = str + str6 + ',';
                }
                str = str.TrimEnd(new char[] { ',' }) + ' ';
            }
            if (buildCommand)
            {
                usedDbCommand.CommandText = str;
                return usedDbCommand;
            }
            return str;
        }

        public void GroupBy(params string[] columns)
        {
            foreach (string str in columns)
            {
                this._groupByColumns.Add(str);
            }
        }

        public void SelectAllColumns()
        {
            this._selectedColumns.Clear();
        }

        public void SelectColumn(string column)
        {
            this._selectedColumns.Clear();
            this._selectedColumns.Add(column);
        }

        public void SelectColumns(params string[] columns)
        {
            this._selectedColumns.Clear();
            foreach (string str in columns)
            {
                this._selectedColumns.Add(str);
            }
        }

        public void SelectCount()
        {
            this.SelectColumn("count(1)");
        }

        public void SelectFromTable(string table)
        {
            this._selectedTables.Clear();
            this._selectedTables.Add(table);
        }

        public void SelectFromTables(params string[] tables)
        {
            this._selectedTables.Clear();
            foreach (string str in tables)
            {
                this._selectedTables.Add(str);
            }
        }

        public void SetDbProviderFactory(DbProviderFactory factory)
        {
            this._dbProviderFactory = factory;
        }

        public bool Distinct
        {
            get
            {
                return this._distinct;
            }
            set
            {
                this._distinct = value;
            }
        }

        public WhereStatement Having
        {
            get
            {
                return this._havingStatement;
            }
            set
            {
                this._havingStatement = value;
            }
        }

        public string[] SelectedColumns
        {
            get
            {
                if (this._selectedColumns.Count > 0)
                {
                    return this._selectedColumns.ToArray();
                }
                return new string[] { "*" };
            }
        }

        public string[] SelectedTables
        {
            get
            {
                return this._selectedTables.ToArray();
            }
        }

        public TopClause TopClause
        {
            get
            {
                return this._topClause;
            }
            set
            {
                this._topClause = value;
            }
        }

        public int TopRecords
        {
            get
            {
                return this._topClause.Quantity;
            }
            set
            {
                this._topClause.Quantity = value;
                this._topClause.Unit = TopUnit.Records;
            }
        }

        public WhereStatement Where
        {
            get
            {
                return this._whereStatement;
            }
            set
            {
                this._whereStatement = value;
            }
        }

        internal WhereStatement WhereStatement
        {
            get
            {
                return this._whereStatement;
            }
            set
            {
                this._whereStatement = value;
            }
        }
    }

}
